use std::{
    // convert::Infallible,
    error::Error,
    fmt::{Debug, Display},
};

use faststr::FastStr;

// use crate::RedisResult;

// pub trait ErrContext<T, E> {
//     fn throw<S: ToString>(self, str: S) -> RedisResult<T>;
// }

// impl<T> ErrContext<T, Infallible> for Option<T> {
//     fn throw<S: ToString>(self, str: S) -> RedisResult<T> {
//         if let Some(val) = self {
//             Ok(val)
//         } else {
//             Err(RedisError::Other(str.to_string()))
//         }
//     }
// }

// impl<T, E> ErrContext<T, E> for Result<T, E>
// where
//     E: std::error::Error + Sized,
// {
//     fn throw<S: ToString>(self, str: S) -> RedisResult<T> {
//         match self {
//             Ok(val) => Ok(val),
//             Err(err) => {
//                 // if std::any::type_name_of_val(&err) == "xnebula_common::AppError" {
//                 //     let a: Box<dyn std::any::Any> = Box::from(err);
//                 //     let temp: Box<AppError> = a..downcast::<AppError>().unwrap();
//                 //     // Err(*temp)
//                 //     //    let result=  temp.unwrap();
//                 //     todo!()
//                 // } else {
//                 #[cfg(windows)]
//                 {
//                     Err(RedisError::Other(format!(
//                         "{}\r\n\tSource: {}",
//                         err.to_string(),
//                         str.to_string()
//                     )))
//                 }
//                 #[cfg(unix)]
//                 {
//                     Err(RedisError::Other(format!(
//                         "{}\n\tSource: {}",
//                         err.to_string(),
//                         str.to_string()
//                     )))
//                 }
//                 // }
//             }
//         }
//     }
// }

#[derive(Debug, Clone)]
pub enum RedisError {
    NoStream,
    Connect(String),
    RespSerialize(String),
    RespDeserialize(String),
    SerdeDeserialize(String),
    WaitReceive(String),
    Send(String),
    Response(String),
    SocketDisconneced(String),
    CommandFormat(String),
    Other(String),
}

impl RedisError {
    pub(crate) fn new_resp_deserialize<T: Into<FastStr>>(
        message: T,
        err: impl Error,
    ) -> RedisError {
        #[cfg(windows)]
        {
            RedisError::RespDeserialize(format!(
                "{}\r\n\tSource: {}",
                message.into(),
                err.to_string()
            ))
        }
        #[cfg(unix)]
        {
            RedisError::RespDeserialize(format!(
                "{}\n\tSource: {}",
                message.into(),
                err.to_string()
            ))
        }
    }

    pub(crate) fn new_serde_deserialize<T: Into<FastStr>>(
        message: T,
        err: impl Error,
    ) -> RedisError {
        #[cfg(windows)]
        {
            RedisError::SerdeDeserialize(format!(
                "{}\r\n\tSource: {}",
                message.into(),
                err.to_string()
            ))
        }
        #[cfg(unix)]
        {
            RedisError::SerdeDeserialize(format!(
                "{}\n\tSource: {}",
                message.into(),
                err.to_string()
            ))
        }
    }

    pub(crate) fn new_connect<T: Into<FastStr>>(message: T, err: impl Error) -> RedisError {
        #[cfg(windows)]
        {
            RedisError::Connect(format!(
                "{}\r\n\tSource: {}",
                message.into(),
                err.to_string()
            ))
        }
        #[cfg(unix)]
        {
            RedisError::Connect(format!("{}\n\tSource: {}", message.into(), err.to_string()))
        }
    }

    pub(crate) fn new_wait_receive<T: Into<FastStr>>(message: T, err: impl Error) -> RedisError {
        #[cfg(windows)]
        {
            RedisError::WaitReceive(format!(
                "{}\r\n\tSource: {}",
                message.into(),
                err.to_string()
            ))
        }
        #[cfg(unix)]
        {
            RedisError::WaitReceive(format!("{}\n\tSource: {}", message.into(), err.to_string()))
        }
    }

    pub(crate) fn new_response<T: Into<FastStr>>(message: T, err: impl Error) -> RedisError {
        #[cfg(windows)]
        {
            RedisError::Response(format!(
                "{}\r\n\tSource: {}",
                message.into(),
                err.to_string()
            ))
        }
        #[cfg(unix)]
        {
            RedisError::Response(format!("{}\n\tSource: {}", message.into(), err.to_string()))
        }
    }

    pub(crate) fn new_socket_disconnected<T: Into<FastStr>>(
        message: T,
        err: impl Error,
    ) -> RedisError {
        #[cfg(windows)]
        {
            RedisError::SocketDisconneced(format!(
                "{}\r\n\tSource: {}",
                message.into(),
                err.to_string()
            ))
        }
        #[cfg(unix)]
        {
            RedisError::SocketDisconneced(format!(
                "{}\n\tSource: {}",
                message.into(),
                err.to_string()
            ))
        }
    }
}

// impl Debug for RedisError {
//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//         // f.debug_struct("RedisError").finish()
//         f.write_str("1234err")
//     }
// }

impl Display for RedisError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // f.write_fmt(format_args!("{}", self))
        match self {
            RedisError::NoStream => f.write_str("[NoStream]"),
            RedisError::RespSerialize(str) => f.write_str(&format!("[RespSerialize] {str}")),
            RedisError::RespDeserialize(str) => f.write_str(&format!("[RespDeserialize] {str}")),
            RedisError::SerdeDeserialize(str) => f.write_str(&format!("[SerdeDeserialize] {str}")),
            RedisError::Connect(str) => f.write_str(&format!("[Connect] {str}")),
            RedisError::WaitReceive(str) => f.write_str(&format!("[WaitReceive] {str}")),
            RedisError::Send(str) => f.write_str(&format!("[Send] {str}")),
            RedisError::Response(str) => f.write_str(&format!("[Response] {str}")),
            RedisError::SocketDisconneced(str) => {
                f.write_str(&format!("[SocketDisconneced] {str}"))
            }
            RedisError::CommandFormat(str) => f.write_str(&format!("[CommandFormat] {str}")),
            RedisError::Other(str) => f.write_str(str),
        }
    }
}

impl serde::ser::Error for RedisError {
    fn custom<T>(msg: T) -> Self
    where
        T: Display,
    {
        RedisError::RespSerialize(msg.to_string())
    }
}

impl serde::de::Error for RedisError {
    fn custom<T>(msg: T) -> Self
    where
        T: Display,
    {
        RedisError::RespDeserialize(msg.to_string())
    }
}

impl Error for RedisError {}
